module tb_cpscrypto();

reg [31:0] PWDATA;
reg [3:0] PADDR;
reg PCLK, PRESETn;
reg PSEL, PENABLE, PWRITE;

wire [31:0] PRDATA;
wire PREADY, PSLVERR, interrupt;

reg [127:0] cipher, golden_cipher, block_rd, key_rd;
reg [31:0] config_rd;
  
CPSCrypto CRYPTO
(
  .PRDATA     ( PRDATA     ),
  .PREADY     ( PREADY     ),
  .PSLVERR    ( PSLVERR    ),
  .INT_CRYPTO ( interrupt  ),
  .PWDATA     ( PWDATA     ),    
  .PADDR      ( PADDR      ),     
  .PSEL       ( PSEL       ),      
  .PENABLE    ( PENABLE    ),   
  .PWRITE     ( PWRITE     ),    
  .PCLK       ( PCLK       ),      
  .PRESETn    ( PRESETn    )
);

localparam BLOCK = 2'b00;
localparam KEY  = 2'b01;

task reset;
  begin
    PCLK = 0;
    PSEL = 0;
    PWRITE = 0;
    PRESETn = 0;
    #50
    PRESETn = 1;
  end
endtask

task apb_write;
  input [128:0] block;
  input block_type;
  reg [3:0] offset;
  begin
    //TRANSFER 1
    offset = (block_type == BLOCK) ? 0 : 4;
    @(negedge PCLK);
    PSEL = 1;
    PWRITE = 1;
    PADDR = 3 + offset;
    PWDATA = {block[7:0], block[15:8], block[23:16], block[31:24]};
    @(posedge PCLK);
    PENABLE = 1;
    @(posedge PCLK);
    PENABLE  = 0;
    //TRANSFER 2
    @(negedge PCLK);
    PSEL = 1;
    PWRITE = 1;
    PADDR = 2 + offset;
    PWDATA = {block[39:32], block[47:40], block[55:48], block[63:56]};
    @(posedge PCLK);
    PENABLE = 1;
    @(posedge PCLK);
    PENABLE  = 0;
    //TRANSFER3
    @(negedge PCLK);
    PSEL = 1;
    PWRITE = 1;
    PADDR = 1 + offset;
    PWDATA = {block[71:64], block[79:72], block[87:80], block[95:88]};
    @(posedge PCLK);
    PENABLE = 1;
    @(posedge PCLK);
    PENABLE  = 0;
    //TRANSFER 4
    @(negedge PCLK);
    PSEL = 1;
    PWRITE = 1;
    PADDR = offset;
    PWDATA = {block[103:96], block[111:104], block[119:112], block[127:120]};
    @(posedge PCLK);
    PENABLE = 1;
    @(posedge PCLK);
    PENABLE  = 0;
    PSEL = 0;
  end  
endtask

task apb_read;
  input block_type;
  output [127:0] block;
  reg [3:0] offset;
  begin
    offset = (block_type == BLOCK) ? 0 : 4;
    //TRANSFER 1
    @(negedge PCLK);
    PWRITE = 0;
    PADDR = 3  + offset;
    PSEL = 1;
    @(posedge PCLK);
    PENABLE = 1;
    @(posedge PCLK);
    {block[7:0], block[15:8], block[23:16], block[31:24]} = PRDATA;
    PENABLE = 0;
    //TRANSFER 2
    @(negedge PCLK);
    PWRITE = 0;
    PADDR = 2 + offset;
    PSEL = 1;
    @(posedge PCLK);
    PENABLE = 1;
    @(posedge PCLK);
    {block[39:32], block[47:40], block[55:48], block[63:56]} = PRDATA;
    PENABLE = 0;
    //TRANSFER 3
    @(negedge PCLK);
    PWRITE = 0;
    PADDR = 1 + offset;
    PSEL = 1;
    @(posedge PCLK);
    PENABLE = 1;
    @(posedge PCLK);
    {block[71:64], block[79:72], block[87:80], block[95:88]} = PRDATA;
    PENABLE = 0;
    //TRANSFER 4
    @(negedge PCLK);
    PWRITE = 0;
    PADDR = offset;
    PSEL = 1;
    @(posedge PCLK);
    PENABLE = 1;
    @(posedge PCLK);
    {block[103:96], block[111:104], block[119:112], block[127:120]} = PRDATA;
    PENABLE = 0;
    PSEL = 0;
  end  
endtask

task config_core;
  input [31:0] conf;
  begin
    @(negedge PCLK);
    PSEL = 1;
    PWRITE = 1;
    PADDR = 4'd8;
    PWDATA = conf;
    @(posedge PCLK);
    PENABLE = 1;
    @(posedge PCLK);
    PENABLE  = 0;
  end
endtask

task read_config;
  output [31:0] conf;
  begin
    @(negedge PCLK);
    PSEL = 1;
    PWRITE = 0;
    PADDR = 4'd8;
    @(posedge PCLK);
    @(posedge PCLK);
    conf = PRDATA;
    PSEL = 0;
    PENABLE = 0;
  end
endtask

task check_block;
  input [127:0] block;
  input [127:0] golden_block;
  input integer test_number;
  begin
    if(block != golden_block)
      begin
        $display("Test Case %0d failed!", test_number);
        $display("Expected:%x", golden_block);
        $display("Obtained:%x", block);
        $stop;
     end
   else
     $display("Test Case %0d passed!!", test_number);
  end
endtask

initial
  begin
    reset;
  /*
  //=============================================================================
  // 0) Test Case 0
  //     - Operation: Read and Write Registers
  //     - Plaintext : 32_43_f6_a8_88_5a_30_8d_31_31_98_a2_e0_37_07_34
  //     - Secret Key: 2b_7e_15_16_28_ae_d2_a6_ab_f7_15_88_09_cf_4f_3c
  //     - Ciphertext: 39_25_84_1d_02_dc_09_fb_dc_11_85_97_19_6a_0b_32
  //=============================================================================
    config_core(32'h00_00_00_01);
    read_config(config_rd);
    apb_write(128'h32_43_f6_a8_88_5a_30_8d_31_31_98_a2_e0_37_07_34, BLOCK);
    apb_write(128'h2b_7e_15_16_28_ae_d2_a6_ab_f7_15_88_09_cf_4f_3c, KEY);
    apb_read(BLOCK, block_rd);
    apb_read(KEY, key_rd);
    if(config_rd != 32'h00_00_00_01)
       begin
         $display("Test Case 0 failed!");
         $display("Expected:%x",32'h00_00_00_01);
         $display("Obtained:%x",config_rd);
         $stop;
       end
       
      if(block_rd != 128'h32_43_f6_a8_88_5a_30_8d_31_31_98_a2_e0_37_07_34)
       begin
         $display("Test Case 0 failed!");
         $display("Expected:%x",128'h32_43_f6_a8_88_5a_30_8d_31_31_98_a2_e0_37_07_34);
         $display("Obtained:%x",block_rd);
         $stop;
       end
      
      if(key_rd!= 128'h2b_7e_15_16_28_ae_d2_a6_ab_f7_15_88_09_cf_4f_3c)
       begin
         $display("Test Case 0 failed!");
         $display("Expected:%x",128'h2b_7e_15_16_28_ae_d2_a6_ab_f7_15_88_09_cf_4f_3c);
         $display("Obtained:%x",key_rd);
         //$stop;
       end
      else
       $display("Test Case 0 passed!!");
      */
       
  //=============================================================================
  // 1) Test Case 1
  //     - Operation: Encryption
  //     - Plaintext : 32_43_f6_a8_88_5a_30_8d_31_31_98_a2_e0_37_07_34
  //     - Secret Key: 2b_7e_15_16_28_ae_d2_a6_ab_f7_15_88_09_cf_4f_3c
  //     - Ciphertext: 39_25_84_1d_02_dc_09_fb_dc_11_85_97_19_6a_0b_32
  //=============================================================================
    apb_write(128'h32_43_f6_a8_88_5a_30_8d_31_31_98_a2_e0_37_07_34, BLOCK);
    apb_write(128'h2b_7e_15_16_28_ae_d2_a6_ab_f7_15_88_09_cf_4f_3c, KEY);
    golden_cipher = 128'h39_25_84_1d_02_dc_09_fb_dc_11_85_97_19_6a_0b_32;
    config_core(32'h00_00_00_0d);
    @(posedge interrupt);
    @(posedge PCLK);
    apb_read(BLOCK, cipher);
    check_block(cipher, golden_cipher, 1);
    
  //=============================================================================
  // 2) Test Case 2
  //     - Operation: Encryption
  //     - Plaintext : 00_11_22_33_44_55_66_77_88_99_aa_bb_cc_dd_ee_ff
  //     - Secret Key: 00_01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f
  //     - Ciphertext: 69_c4_e0_d8_6a_7b_04_30_d8_cd_b7_80_70_b4_c5_5a
  //=============================================================================
    @(negedge PCLK);
    apb_write(128'h00_11_22_33_44_55_66_77_88_99_aa_bb_cc_dd_ee_ff, BLOCK);
    apb_write(128'h00_01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f, KEY);
    golden_cipher = 128'h69_c4_e0_d8_6a_7b_04_30_d8_cd_b7_80_70_b4_c5_5a;
    config_core(32'h00_00_00_0f);
    @(posedge interrupt);
    @(posedge PCLK);
    apb_read(BLOCK, cipher);
    check_block(cipher, golden_cipher, 2);
  
 //=============================================================================
  // 3) Test Case 3
  //     - Operation: Decryption
  //     - Ciphertext: 69_c4_e0_d8_6a_7b_04_30_d8_cd_b7_80_70_b4_c5_5a
  //     - Secret Key: 13_11_1d_7f_e3_94_4a_17_f3_07_a7_8b_4d_2b_30_c5
  //     - Plaintext : 00_11_22_33_44_55_66_77_88_99_aa_bb_cc_dd_ee_ff
  //=============================================================================
    @(negedge PCLK);
    apb_write(128'h13_11_1d_7f_e3_94_4a_17_f3_07_a7_8b_4d_2b_30_c5, KEY);
    golden_cipher = 128'h00_11_22_33_44_55_66_77_88_99_aa_bb_cc_dd_ee_ff;
    config_core(32'h00_00_00_05);
    @(posedge interrupt);
    @(posedge PCLK);
    apb_read(BLOCK, cipher);
    check_block(cipher, golden_cipher, 3);
    
 //=============================================================================
  // 4) Test Case 4
  //     - Operation: Key Expansion
  //     - Secret Key   : 00_01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f
  //     - Expanded Key : 13_11_1d_7f_e3_94_4a_17_f3_07_a7_8b_4d_2b_30_c5
  //=============================================================================
    @(negedge PCLK);
    apb_write(128'h00_01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f, KEY);
    config_core(32'h00_00_00_3d);
    golden_cipher = 128'h13_11_1d_7f_e3_94_4a_17_f3_07_a7_8b_4d_2b_30_c5;
    @(posedge interrupt);
    @(posedge PCLK);
    apb_read(KEY, cipher);
    check_block(cipher, golden_cipher, 4);
   
 //=============================================================================
  // 5) Test Case 5
  //     - Operation: Decryption + Key Expansion
  //     - Ciphertext: 69_c4_e0_d8_6a_7b_04_30_d8_cd_b7_80_70_b4_c5_5a
  //     - Secret Key: 00_01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f
  //     - Plaintext : 00_11_22_33_44_55_66_77_88_99_aa_bb_cc_dd_ee_ff
  //=============================================================================
    apb_write(128'h69_c4_e0_d8_6a_7b_04_30_d8_cd_b7_80_70_b4_c5_5a, BLOCK);
    apb_write(128'h00_01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f, KEY);
    config_core(32'h00_00_00_17);
    golden_cipher = 128'h00_11_22_33_44_55_66_77_88_99_aa_bb_cc_dd_ee_ff;
    @(posedge interrupt);
    @(posedge PCLK);
    config_core(32'h00_00_00_02);
    apb_read(BLOCK, cipher);
    check_block(cipher, golden_cipher, 5);
     
  //=============================================================================
  // 6) Test Case 6
  //     - Operation: Writing to block registers during core operation
  //     - Plaintext : 00_11_22_33_44_55_66_77_88_99_aa_bb_cc_dd_ee_ff
  //     - Secret Key: 00_01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f
  //     - Ciphertext: 69_c4_e0_d8_6a_7b_04_30_d8_cd_b7_80_70_b4_c5_5a
  //=============================================================================
    @(negedge PCLK);
    apb_write(128'h00_11_22_33_44_55_66_77_88_99_aa_bb_cc_dd_ee_ff, BLOCK);
    apb_write(128'h00_01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f, KEY);
    golden_cipher = 128'h69_c4_e0_d8_6a_7b_04_30_d8_cd_b7_80_70_b4_c5_5a;
    config_core(32'h00_00_00_0d);
    @(posedge PCLK);
    @(posedge PCLK);
    @(posedge PCLK);
    @(posedge PCLK);
    apb_write(128'h00_11_22_33_44_55_66_77_88_99_aa_bb_cc_dd_ee_ff, BLOCK);
    @(posedge PCLK);
    @(posedge PCLK);
    @(posedge PCLK);
    @(posedge PCLK);
    apb_write(128'h00_01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f, KEY);
    apb_read(BLOCK, cipher);
    read_config(config_rd);
    if(config_rd != 32'h00_00_00_0d)
       begin
         $display("Test Case 6 failed!");
         $display("Expected:%x",32'h00_00_00_01);
         $display("Obtained:%x",config_rd);
         $stop;
       end
    config_core(32'h11_11_11_11);
    @(posedge interrupt);
    @(posedge PCLK);
    apb_read(BLOCK, cipher);
    check_block(cipher, golden_cipher, 6);
  
  //=============================================================================
  // 7) Test Case 2
  //     - Operation: sequential Encryptions with the same Key
  //     - Plaintext_1 : 80_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00
  //     - Plaintext_2 : c0_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00
  //     - Plaintext_3 :  ff_ff_ff_ff_fc_00_00_00_00_00_00_00_00_00_00_00
  //     - Secret Key  : 00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00
  //     - Ciphertext_1: 3a_d7_8e_72_6c_1e_c0_2b_7e_bf_e9_2b_23_d9_ec_34
  //     - Ciphertext_2: aa_e5_93_9c_8e_fd_f2_f0_4e_60_b9_fe_71_17_b2_c2
  //     - Ciphertext_3:  aa_e5_93_9c_8e_fd_f2_f0_4e_60_b9_fe_71_17_b2_c2
  //=============================================================================
    @(negedge PCLK);
    apb_write(128'h80_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00, BLOCK);
    apb_write(128'h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00, KEY);
    golden_cipher = 128'h3a_d7_8e_72_6c_1e_c0_2b_7e_bf_e9_2b_23_d9_ec_34;
    config_core(32'h00_00_00_0f);
    @(posedge interrupt);
    @(posedge PCLK);
    apb_read(BLOCK, cipher);
    check_block(cipher, golden_cipher, 7);
    
    @(negedge PCLK);
    apb_write(128'hc0_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00, BLOCK);
    golden_cipher = 128'haa_e5_93_9c_8e_fd_f2_f0_4e_60_b9_fe_71_17_b2_c2;
    config_core(32'h00_00_00_0f);
    @(posedge interrupt);
    @(posedge PCLK);
    apb_read(BLOCK, cipher);
    check_block(cipher, golden_cipher, 7);
    
    @(negedge PCLK);
    apb_write(128'hff_ff_ff_ff_fc_00_00_00_00_00_00_00_00_00_00_00, BLOCK);
    golden_cipher = 128'h21_5a_41_ee_44_2f_a9_92_a6_e3_23_98_6d_ed_3f_68;
    config_core(32'h00_00_00_0f);
    @(posedge interrupt);
    @(posedge PCLK);
    apb_read(BLOCK, cipher);
    check_block(cipher, golden_cipher, 7);
    $stop;
  end

always #10
  PCLK = !PCLK;
endmodule
